#include <iostream>
#include <sstream>

#include "generic.h"
#include "model.h"
#include "model_link.h"
#include "predictors/predictor_base.h"
#include "output_base.h"

using namespace std;

Model::Model(Predictor_base *p)
{
  _predictor = p;
  _id = Generic::get_id();
  _active = false;

  set_output_link(new Model_link());

  set_string_id("M: " + p->get_string_id());
}

Model::Model(Model *m)
{
  _models.push_back(m);
  _id = Generic::get_id();
}

Model::~Model()
{
}

void Model::set_string_id(string id)
{
  _string_id = id;
}

std::string Model::get_string_id(void)
{
  return _string_id;
}

// inhibitor lnks
void Model::add_inhibitor_link(Model *m)
{
  _inhibitor_links.push_back(m->get_output_link());
}

void Model::add_inhibitor_link(Model_link *l)
{
  _inhibitor_links.push_back(l);
}

void Model::remove_inhibitor_link(Model *m)
{
}

void Model::remove_inhibitor_link(Model_link *l)
{
}

// activator lnks
void Model::add_activator_link(Model *m)
{
  _activator_links.push_back(m->get_output_link());
}

void Model::add_activator_link(Model_link *l)
{
  _activator_links.push_back(l);
}

void Model::remove_activator_link(Model *m)
{
}

void Model::remove_activator_link(Model_link *l)
{
}

void Model::calculate_error(float t)
{
  if (_models.empty()) {
    _predictor->calculate_error(t);
  }
}

bool Model::is_active(void)
{
  return _active;
}

void Model::predict(float t)
{
  if (_models.empty()) {
    _predictor->predict(t);
  }
  else {
    for (int i = 0; i < (int) _models.size(); ++i) {
      _models[i]->predict(t);
    }
  }
}

Predictor_base *Model::get_predictor(void)
{
  return _predictor;
}

string Model::info(void)
{
  stringstream ss;

  ss << "ID: " << _string_id << " (" << _id << ")" << endl;
  ss << "Activators: " << endl;
  ss << "Inhibitors: " << endl;
  if (_predictor == NULL) {
    ss << "Predictor: NONE" << endl;
  }
  else {
    ss << "Predictor:" << endl;
    _predictor->info(ss);
  }
  return ss.str();
}


void Model::reset(void)
{
  if (_models.empty()) {
    _predictor->reset();
  }
  else {
    for (int i = 0; i < (int) _models.size(); ++i) {
      _models[i]->reset();
    }
  }
}

void Model::save_state(float t)
{
  //cout << " # model: save state" << endl;
  if (_models.empty()) {
    _predictor->save_state(t);
  }
  else {
    for (int i = 0; i < (int) _models.size(); ++i) {
      _models[i]->save_state(t);
    }
  }
}

void Model::activate(float critical_error)
{
//  cout << " # model: " << _string_id << ": Activating" << endl;
  if (_models.empty()) {
    _predictor->activate(critical_error);
    if (_predictor->is_active()) {
      _active = true;
//      cout << " # active" << endl;
    }
    else {
      _active = false;
//      cout << " # in-active" << endl;
    }
  }
  else {
    bool sub_models_active = false;
    int N = _models.size();
    for (int i = 0; i < N; ++i) {
      _models[i]->activate(critical_error);
      if (!_models[i]->is_active()) {
        sub_models_active = false;
        break;
      }
    }
    if (sub_models_active) {
      _active = true;
    }
    else {
      _active = false;
    }
  }
}

void Model::activate_output_link(void)
{
  // check if an activator link is active
  int n = _activator_links.size();
  for (int i = 0; i < n; ++i) {
    if (_activator_links[i]->is_active()) {
//      cout << " # model:  " << _string_id << ":  Activating output (activator)"
//          << endl;
      _output_link->activate();
      if (_predictor != NULL) {
        _predictor->activate_output_link();
      }
      return;
    }
  }

  if (_active) {
    // check that none of the inhibitor links are active
    n = _inhibitor_links.size();
    for (int i = 0; i < n; ++i) {
      if (_inhibitor_links[i]->is_active()) {
//        cout << " # model:  " << _string_id
//            << ":  De-activating output (inhibitor)" << endl;
        _output_link->deactivate();
        return;
      }
    }
//    cout << " # model:  " << _string_id << ":  Activating output " << endl;
    _output_link->activate();
    if (_predictor != NULL) {
      _predictor->activate_output_link();
    }
  }
  else {
//    cout << " # model: " << _string_id
//        << ":  De-activating output (predictor not active)" << endl;
    _output_link->deactivate();
  }
}

void Model::set_output_link(Model_link *l)
{
  _output_link = l;
  l -> set_source(this);
}

Model_link *Model::get_output_link(void)
{
  if (_output_link == NULL) {
    cout << "ERROR: output link is NULL" << endl;
    exit(1);
  }
  return _output_link;
}

